
#include <xbi/xbiss.h>

#if 0
#pragma mark Definitions
#endif

struct _XBISecuritySector {
    byte_t * data;
    int      size;
    
    byte_t   version;    
    XBIConsoleChallengeResponseTable * CCRT;
    byte_t   mediaID[_XBIMediaIDLength];
    time_t   authoringDate;
    byte_t   authoringVersion;
    byte_t   authoringSystemID[_XBISSAuthoringSystemIDLength];
    time_t   masteringDate;
    byte_t   masteringVersion;
    byte_t   masteringSystemID[_XBISSMasteringSystemIDLength];
    
    XBIPhysicalFormatInformation * pfi; // Replacement PFI for when disk is unlocked; Points to actual game content
    
};

#if 0
#pragma mark Creation - Copy - Deletion
#endif

XBISecuritySector * XBISSCreateFromBuffer(byte_t * buffer, int size, XBIError * error) {        
    char nullBuffer[4] = {0, 0, 0, 0};
    XBISecuritySector * newSS = NULL;
    
    if (XBIBufferIsEmpty(buffer, size)) {
        XBILog("Security sector buffer is empty, ignoring\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    
    XBILog("Checking SS buffer for integrity\n");
    
    if (size != _XBISecuritySectorSize) {
        XBILog("Security sector buffer doesn't have the right size\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    
    if (memcmp(buffer + 0x2D4, nullBuffer, 4) != 0) { // Check for null value at a magic offset
        XBILog("This does not appear to be a valid SS buffer\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    if (memcmp(buffer + 0x2D0, nullBuffer, 4) == 0) { // Check for non-null value at CPR_MAI
        XBILog("This is not an Xtreme SS buffer (no CPR_MAI)\n");
        *error = kXBIFileWrongFormat;
        goto error;
    }
    
    XBILog("SS buffer is correct length and has CPR_MAI\n");
    
    newSS = malloc(sizeof(XBISecuritySector));
    if (newSS == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memset(newSS, 0, sizeof(XBISecuritySector));
    
    newSS->data = malloc(_XBISecuritySectorSize);
    if (newSS->data == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memcpy(newSS->data, buffer, _XBISecuritySectorSize);
    
    newSS->size = size;
    
    switch (newSS->data[0x65f]) {
        case 1:
            XBILog("Xbox 1 SS version\n");
            newSS->version = kXBIXbox1SS;
            break;
        case 2:
            XBILog("Xbox 360 SS version\n");
            newSS->version = kXBIXbox360SS;
            break;
        default:
            XBILog("Unknown SS version !\n");
            *error = kXBIFileWrongFormat;
            goto error;
            break;
    }
    XBILog("SS version : %d\n", newSS->version);
    
    XBIAssert(_XBISecuritySectorSize == _XBIPhysicalFormatInformationSize, "Cannot create a PFI from a SS");
    newSS->pfi = XBIPFICreateFromBuffer(kXBIPFIGeneralVersion, newSS->data, _XBIPhysicalFormatInformationSize, error);
    
    switch(newSS->data[0x300]) {
        case 1:
            XBILog("Xbox 1 style CCRT\n");
            newSS->CCRT = XBICCRTCreateFromBuffer(kXBIXbox1CCRT, newSS->data+0x300, 255, error);
            break;
        case 2:
            XBILog("Xbox 360 style CCRT\n");
            newSS->CCRT = XBICCRTCreateFromBuffer(kXBIXbox360CCRT, newSS->data+0x300, 256, error);
            break;
        default:
            XBILog("Unknown CCRT type!\n");
            *error = kXBIFileWrongFormat;
            goto error;
            break;
    }
    
    // Media ID
    if (newSS->version == kXBIXbox360SS) {
        memcpy(newSS->mediaID, newSS->data + 0x460, _XBIMediaIDLength);
        XBILog("MediaID : %s\n", newSS->mediaID);
    }
    
    // Win32 is little-endian
    newSS->authoringDate = XBIWin32FileTimeToUnixTime(XBIReadLittleEndianInt64AtAddress(newSS->data + 0x49F));
    XBILog("Authoring date : %s", ctime(&newSS->authoringDate));

    
    newSS->authoringVersion = newSS->data[0x4BA];
    XBILog("Authoring version : %d\n", newSS->authoringVersion);

    memcpy(newSS->authoringSystemID, newSS->data + 0x4BB, _XBISSAuthoringSystemIDLength);
    XBILog("Authoring system ID : %s\n", newSS->authoringSystemID);
    
    if (newSS->version == kXBIXbox1SS) {
        // TODO: implement
        XBILog("Should get Xbox 1 SS datetime\n");
    }

    newSS->masteringDate = XBIWin32FileTimeToUnixTime(XBIReadLittleEndianInt64AtAddress(newSS->data + 0x5DF));
    XBILog("Mastering date : %s", ctime(&newSS->masteringDate));

    newSS->masteringVersion = newSS->data[0x5FA];
    XBILog("Mastering version : %d\n", newSS->authoringVersion);

    memcpy(newSS->masteringSystemID, newSS->data + 0x5FB, _XBISSMasteringSystemIDLength);
    XBILog("Mastering system ID : %s\n", newSS->masteringSystemID);

    /* 0x60b : 86 bytes : Suspected : Hardware system signature and hash:  Maybe 22 hash/64 signature?
       0x660 - end
      */
    
    return newSS;
    
error:
    XBISSDelete(newSS);
    return NULL;
}

XBISecuritySector * XBISSCreateFromFile(FILE * file, XBIError * error) {
    size_t bufferSize = 0;
    byte_t * buffer = XBICreateBufferFromFile(file, &bufferSize);
    return XBISSCreateFromBuffer(buffer, bufferSize, error);
}

XBISecuritySector * XBISSCopy(XBISecuritySector * ss) {
    // TODO: Implement real copy
    return ss;
}

void XBISSDelete(XBISecuritySector * ss) {
    if (ss != NULL) {
        if (ss->data != NULL) {
            free(ss->data);
            ss->data = NULL;
        }        
        free(ss);
    }
}

#if 0
#pragma mark Data manipulation
#endif

off_t XBISSGetDataPartitionStart(XBISecuritySector * ss) {
    if (ss != NULL && ss->pfi != NULL)
        return ((off_t)(XBIPFIGetStartPSN(ss->pfi)) - _XBIXbox360StartPSN) * kXBIDVDSectorSize;
    return -1;
}

off_t XBISSGetDiscSize(XBISecuritySector * ss) {
    if (ss != NULL && ss->pfi != NULL)
        return XBIPFIGetDiscSize(ss->pfi);
    return -1;    
}

uint32_t XBISSGetCRC32(XBISecuritySector * ss) {
    if (ss == NULL || ss->data == NULL || ss->size == 0)
        return 0;
    return XBIComputeCRC32(ss->data, ss->size);
}


#if 0
#pragma mark Accessors
#endif

time_t XBISSGetAuthoringDate(XBISecuritySector * ss) {
    if (ss != NULL)
        return ss->authoringDate;
    return -1;
}

byte_t * XBISSGetMediaID(XBISecuritySector * ss) {
    if (ss != NULL)
        return ss->mediaID;
    return NULL;
}

byte_t * XBISSGetData(XBISecuritySector * ss) {
    if (ss != NULL)
        return ss->data;
    return NULL;    
}

size_t XBISSGetSize(XBISecuritySector * ss) {
    if (ss != NULL) {
        XBIAssert(ss->size == _XBISecuritySectorSize, "Wrong size for Security sector\n");
        return ss->size;
    }
    return -1;
}

XBIPhysicalFormatInformation * XBISSGetPFI(XBISecuritySector * ss) {
    if (ss != NULL)
        return ss->pfi;
    return NULL;
}
